package xyz.hubery.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import xyz.hubery.constant.LoginConstant;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private JdbcTemplate jdbcTemplate;



    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 获取登录类型
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String loginType = requestAttributes.getRequest().getParameter("login_type");
        if (StringUtils.isEmpty(loginType)) {
            throw new AuthenticationServiceException("请添加 login_type 参数");
        }
        String grantType = requestAttributes.getRequest().getParameter("grant_type");
        if (LoginConstant.REFRESH_TOKEN.equals(grantType.toUpperCase())){
            username = adjustUsername(username, loginType); // 为 refresh_token时，需要将 id->username
        }


        try {
            switch (loginType) {
                case LoginConstant.ADMIN_TYPE:
                    return loadSysUserByName(username);
                case LoginConstant.MEMBER_TYPE:
                    return loadMemUserByName(username);
                default:
                    throw new AuthenticationServiceException("占卜支持该登录类型：" + loginType);
            }
        } catch (IncorrectResultSizeDataAccessException e) {
            throw new UsernameNotFoundException("用户不存在");
        }
    }

    /**
     * 纠正在 refresh 场景下的登录问题
     *
     * @param username
     * @param loginType
     * @return
     */
    private String adjustUsername(String username, String loginType) {
        if (LoginConstant.ADMIN_TYPE.equals(loginType)) {
            return jdbcTemplate.queryForObject(LoginConstant.QUERY_ADMIN_USER_WITH_ID, String.class,
                    username);
        }
        if (LoginConstant.MEMBER_TYPE.equals(loginType)) {
            return jdbcTemplate.queryForObject(LoginConstant.QUERY_MEMBER_USER_WITH_ID,
                    String.class, username);
        }
        return username;
    }

    /**
     * 根据用户名查询管理员
     * @param username
     * @return
     */
    public UserDetails loadSysUserByName(String username) {
        // 1、根据用户名查询用户
        return jdbcTemplate.queryForObject(LoginConstant.QUERY_ADMIN_SQL, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int i) throws SQLException {
                if(rs.wasNull()) {
                    throw new UsernameNotFoundException("用户不存在");
                }
                long id = rs.getLong("id");
                String password = rs.getString("password");
                int status = rs.getInt("status");
                // 3、封装为UserDetails对象
                return new User(
                        String.valueOf(id),
                        password,
                        status == 1,
                        true,
                        true,
                        true,
                        getPermissions(id)// 2、查询用户的权限
                );

            }
        }, username);
    }

    /**
     * 根据用户id查询权限列表
     * @param id
     * @return
     */
    private Collection<? extends GrantedAuthority> getPermissions(Long id) {
        List<String> permissions = null;
        // 查询用户是否为管理员
        String code = jdbcTemplate.queryForObject(LoginConstant.QUERY_ROLE_CODE_SQL, String.class, id);
        if (LoginConstant.ADMIN_ROLE_CODE.equals(code)) { // 当前用户为管理员
            // 查询所有权限
            permissions = jdbcTemplate.queryForList(LoginConstant.QUERY_ALL_PERMISSIONS, String.class);
        } else { // 普通用户
            // 根据用户id查询权限
            permissions = jdbcTemplate.queryForList(LoginConstant.QUERY_PERMISSION_SQL, String.class, id);
        }
        // 转换为GrantedAuthority
        if (permissions == null || permissions.isEmpty()) {
            return Collections.emptySet();
        }
        return permissions.stream()
                .distinct()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toSet());
    }


    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 根据用户名查询用户（会员用户
     * @param username
     * @return
     */
    private UserDetails loadMemUserByName(String username) {

        UserDetails userDetails = jdbcTemplate.queryForObject(LoginConstant.QUERY_MEMBER_SQL, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int i) throws SQLException {
                if(rs.wasNull()) {
                    throw new UsernameNotFoundException("用户不存在");
                }
                long id = rs.getLong("id");
                String password = rs.getString("password");
                int status = rs.getInt("status");
                // 3、封装为UserDetails对象
                return new User(
                        String.valueOf(id),
                        password,
                        status == 1,
                        true,
                        true,
                        true,
                        Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER"))
                );
            }
        },username,username);

        System.out.println(userDetails);
        return userDetails;

    }

}
